/* Copyright (c) 2007 John Wells

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */

// Short-cuts
using EntityKeyValuePair = System.Collections.Generic.KeyValuePair<string, object>;

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SkinnedModel;

namespace JohnWells.GameLevel
{
    public class ModelInstance
    {
        #region Fields
        // Public fields

        public enum ModelType
        {
            SkinnedModelType,
            DefaultModelType
        }

        public string name, instanceName;
        public Vector3 position;
        public Vector3 rotation;
        public ModelType type;
        Matrix rotationMatrix;

        private AnimationPlayer animationPlayer;
        private AnimationClip idle_clip, open_clip, close_clip;

        private Model model;
        public  void SetModel (Model model,Level level)
        {
            this.model = model;

            Level.LightEntity myLight = null;
            float lightDistance = 0;
            foreach (Level.LightEntity light in level.lights)
            {
                if (myLight == null)
                {
                    myLight = light;
                    lightDistance = Vector3.Distance(position, light.position);
                }
                else if (Vector3.Distance(position, light.position) < lightDistance)
                {
                    myLight = light;
                    lightDistance = Vector3.Distance(position, light.position);
                }
            }

            Vector4 lightColor = new Vector4(0.2f, 0.2f, 0.2f, 1);
            Vector3 lightPosition = new Vector3(0, 1000, 0);

            if (myLight != null)
            {
                // Make sure the light is not right on top of the model
                Vector3 lightDirection = myLight.position - position;
                lightDirection.Normalize();
                lightPosition = position + lightDirection * 1000;


                if (lightDistance < myLight.falloff)
                {
                    lightColor = myLight.color * myLight.intensity * lightDistance / (myLight.falloff*256f);
                }
                //else lightColor = Vector4.Zero;
            }


            if (type == ModelType.SkinnedModelType)
            {
                // Look up our custom skinning information.
                SkinningData skinningData = model.Tag as SkinningData;

                BoundingBox bb = skinningData.BoundingBox;
                //bb.Min.Y -= 10;
                //ModelBoundingBox = bb;

                if (skinningData == null)
                    throw new InvalidOperationException
                        ("This model does not contain a SkinningData tag.");

                // Create an animation player, and start decoding an animation clip.
                animationPlayer = new AnimationPlayer(skinningData);

                idle_clip = skinningData.AnimationClips["idle"];
                open_clip = skinningData.AnimationClips["open"];
                close_clip = skinningData.AnimationClips["close"];

                animationPlayer.StartClip(idle_clip);
                animationPlayer.loopAnimation = false;
                //currentClip = idle_clip;
            }

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (Effect effect in mesh.Effects)
                {
                    
                    Vector4 ambientLightColor = new Vector4(0.2f, 0.2f, 0.2f, 1);
                    float shininess = .1f;
                    float specularPower = 3.0f;

                    
                    effect.Parameters["AmbientLightColor"].SetValue(ambientLightColor);
                    effect.Parameters["LightColor"].SetValue(lightColor);
                    effect.Parameters["Shininess"].SetValue(shininess);
                    effect.Parameters["SpecularPower"].SetValue(specularPower);
                    effect.Parameters["LightPosition"].SetValue(lightPosition);
                    

                    /*
                    effect.Alpha = 1.0f;
                    effect.DiffuseColor = new Vector3(0.75f, 0.75f, 0.75f);
                    effect.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f);
                    effect.SpecularPower = 5.0f;
                    effect.AmbientLightColor = new Vector3(0.15f, 0.15f, 0.15f);

                    effect.DirectionalLight0.Enabled = true;
                    effect.DirectionalLight0.DiffuseColor = new Vector3(0.75f, 0.75f, 0.75f);
                    effect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(1.0f, -1.0f, -1.0f));
                    effect.DirectionalLight0.SpecularColor = new Vector3(0.75f, 0.75f, 0.75f);

                    effect.DirectionalLight1.Enabled = true;
                    effect.DirectionalLight1.DiffuseColor = new Vector3(0.2f, 0.2f, 0.2f);
                    effect.DirectionalLight1.Direction = Vector3.Normalize(new Vector3(-1.0f, -1.0f, 1.0f));
                    effect.DirectionalLight1.SpecularColor = new Vector3(0.5f, 0.5f, 0.5f);


                    effect.LightingEnabled = true;
                    */



                }
            }

        }

        #endregion

        public ModelInstance(string name, string instanceName, ModelType modelType, Vector3 position, Vector3 rotation)
        {
            this.name = name;
            this.instanceName = instanceName;
            this.type = modelType;
            this.position = position;
            this.rotation = rotation;
        }

        public void OnExit()
        {
            animationPlayer.StartClip(close_clip);
            animationPlayer.loopAnimation = false;
        }

        public void OnEnter()
        {
            animationPlayer.StartClip(open_clip);
            animationPlayer.loopAnimation = false;
        }

        public void Update(GameTime gameTime)
        {
            Vector3 rotation = this.rotation;
            rotation.Y = rotation.Y - 90;
            rotation = rotation * (float)Math.PI / 180f;
            rotationMatrix =
                Matrix.CreateFromYawPitchRoll(rotation.Y, rotation.Z, rotation.X);

            rotationMatrix.Translation = position;

            if (animationPlayer != null) animationPlayer.Update(gameTime.ElapsedGameTime, true, rotationMatrix);
        }

        public void Draw(GraphicsDevice graphics, Vector3 cameraPos, Vector3 cameraTarget)
        {
            // Compute camera matrices.
            Matrix unrotatedView = Matrix.CreateLookAt(
                cameraPos, cameraTarget, Vector3.Up);

            float aspectRatio = (float)graphics.Viewport.Width /
                               (float)graphics.Viewport.Height;

            Matrix view = Matrix.CreateRotationY(MathHelper.ToRadians(0.0f)) *
                          Matrix.CreateRotationX(MathHelper.ToRadians(0.0f)) *
                          unrotatedView;


            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
                                                                    aspectRatio,
                                                                    1,
                                                                    10000);

            //Matrix lightRotationMatrix = Matrix.CreateRotationY(lightRotation);
            Vector3 lightPosition = position + new Vector3(0, 1000, 0);
            //lightPosition = Vector3.Transform(lightPosition, lightRotationMatrix);

            Matrix[] bones = null;

            if (type == ModelType.SkinnedModelType)
            {
                bones = animationPlayer.GetSkinTransforms();
            }
          
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (Effect effect in mesh.Effects)
                {
                    if (type == ModelType.DefaultModelType)
                    {
                        effect.Parameters["World"].SetValue(rotationMatrix);
                        effect.Parameters["View"].SetValue(view);
                        effect.Parameters["Projection"].SetValue(projection);
                    }
                    if (type == ModelType.SkinnedModelType)
                    {
                        effect.Parameters["Bones"].SetValue(bones);
                        effect.Parameters["View"].SetValue(view);
                        effect.Parameters["Projection"].SetValue(projection);
                    }


                    /*
                    effect.World = rotationMatrix;
                    effect.View = view;
                    effect.Projection = projection;
                    */
                    
                    //effect.Parameters["LightPosition"].SetValue(lightPosition);
                }

                mesh.Draw();
            }
        }
    }
}
